#ifndef UDP_SERVER
#define UDP_SERVER

#include <ctime>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <string>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include "pthread.h"
#include "DataRepo.hpp"
#include "Connection_End_Sensor_UDP.hpp"
#include "Connection_End_Client_UDP.hpp"
#include "Connection_End_Server_UDP.hpp"
#include "Message.hpp"
#include "Logger.h"
#include <map>
#include <set>
#include "Config.hpp"

using boost::asio::ip::udp;
typedef boost::shared_ptr< udp::endpoint > ENDPOINT_POINTER;

const 	int		TERRAINPACKETSIZE	= 256;//Make your buffer larger than
const	int		MsgLengh	 		= 128;
char	buf[ TERRAINPACKETSIZE ];
const	int 	pushPeriod			= 3;
const 	int 	TimeOutPeriod 		= 3;


class udp_server
{

public:

	std::string IP_Address;
	std::string m_strName;
	Logger& LOGGER;

	int m_ClientIdx;
	int m_SensorIdx;
	int m_ServerIdx;
	int m_NumberOfServerInSystem;

	DataType m_DataType;

	boost::asio::io_service& m_io_service_;
	udp::socket socket_;
	DataRepo& m_repo;
	udp::endpoint remote_endpoint_;
	boost::asio::deadline_timer timeOutTimer;
	boost::asio::deadline_timer pushTimer;

	std::map< std::string , ENDPOINT_POINTER > m_Server_Endpoint;
	typedef std::map< std::string , DataType > CLIENTNAME_DATATYPE;
	CLIENTNAME_DATATYPE							m_NonAnsweredPulls;//Client Name :: Type asked by the client.

	time_t			m_tLastpushTime;
	bool 			m_bIsDistributed;

	typedef std::map< std::string, Connection_End_Sensor_UDP::ConnectionEnd_Sensor_Pointer >	SENSORS_MAP;
	typedef std::map< std::string, Connection_End_Client_UDP::ConnectionEnd_Client_Pointer >	CLIENTS_MAP;
	typedef std::map< std::string, Connection_End_Server_UDP::ConnectionEnd_Server_Pointer >	SERVERS_MAP;


	std::map< std::string, Connection_End_Sensor_UDP::ConnectionEnd_Sensor_Pointer >	m_Sensors;
	std::map< std::string, Connection_End_Client_UDP::ConnectionEnd_Client_Pointer >	m_Clients;
	std::map< std::string, Connection_End_Server_UDP::ConnectionEnd_Server_Pointer >	m_Servers;

	//Server Ack Map: This map is used at the startup of the server to make sure
	//That all other Servers DID ACKNOWLEDGE the connection this server Inititated
	//( by sending the Entity Message ).
	//--This is deprecated, I handle the Entity messages in messae timeouts (similar to the client)--
	//--The list of sent messages i saved in the non ack map in the server connection object--
	//std::map< std::string , MsgPointer > m_ServerAckMap ;

	//This map maintains what server ( by name ) has what data type.
	//This map will be updated at each UpdateData action mesage ( Type = REQ, action = UpdateData ).
	//Note that for this server, the available data can be queried from the data Local repo.
	std::map< DataType , std::string  > m_DataType_Server;

	udp_server(boost::asio::io_service& io_service,
			  std::string IP ,
			  int port ,
			  DataRepo& repo,
			  Logger& lo,
			  std::string ServerName,
			  DataType tt,
			  std::string otherServerPort,
			  bool DistributedMode ): m_io_service_( io_service ),
			  	  	  	  	  	  	  	socket_(io_service, udp::endpoint(udp::v4(), port)),
			  	  	  	  	  	  	  	m_repo( repo ), LOGGER( lo ),
			  	  	  	  	  	  	  	timeOutTimer( m_io_service_ ),
			  	  	  	  	  	  	  	pushTimer( m_io_service_ )
	{
			IP_Address 	= IP ;
			m_ClientIdx	= 0 ;
			m_SensorIdx	= 0 ;
			m_strName		= ServerName;
			m_DataType		= tt;
			m_bIsDistributed = DistributedMode;

			pthread_mutex_init( &m_SockMutex,		NULL );
			pthread_mutex_init( &m_ClientsMutex,	NULL );
			pthread_mutex_init( &m_SensorsMutext,	NULL );
			pthread_mutex_init( &m_NonAnsweredMapMutex , NULL );
			pthread_mutex_init( &m_ServerMapMutex, NULL );
			start_receive() ;

			if( DistributedMode )
			{
				LOGGER<<"Distributed Mode \n";
				FillInServers();
				ConnectToOtherServers();
			}

			StartPush();
			SetTimeOutTimer();
	};


	~udp_server()
	{
			pthread_mutex_destroy( &m_SockMutex );
			pthread_mutex_destroy( &m_ClientsMutex );
			pthread_mutex_destroy( &m_SensorsMutext );
			pthread_mutex_destroy( &m_NonAnsweredMapMutex );
			pthread_mutex_destroy( &m_ServerMapMutex );
	}



private:

	pthread_mutex_t m_SockMutex;
	pthread_mutex_t m_ClientsMutex;
	pthread_mutex_t m_SensorsMutext;
	pthread_mutex_t m_NonAnsweredMapMutex;
	pthread_mutex_t m_ServerMapMutex;



void ConnectToOtherServers()
{

//	Connection_End_Server_UDP( int connectionID,
//								udp::endpoint& ep ,
//								boost::asio::io_service& io ,
//								std::string Name )
//Wait for other servers to start receiving.
	sleep( 5 );

	std::map< std::string , ENDPOINT_POINTER >::iterator it = m_Server_Endpoint.begin();
	std::string strOtherServerName;
	int MesageID = 0 ;

	for( ;it != m_Server_Endpoint.end(); it++ )
	{
		MsgPointer msgEntity( new Message() );
		//msgEntity->ID 			= m_Servers[  ];
		msgEntity->m_type	= REQ;
		msgEntity->m_DataType	= CSS;
		msgEntity->m_action		= ENTITY;
		msgEntity->data			= "Server";
		msgEntity->m_strSenderID= m_strName;
		msgEntity->ID			= IntToString( MesageID++ );

		//Now send the entity mesage to the Remote server.
		SendDataToServer( msgEntity, *(it->second), it->first, true );

//		std::string mm = msgEntity->Encode();
//		udp::socket sock( m_io_service_ );
//		sock.connect( *(it->second ) );
//		LockServerAckMap();
//		m_ServerAckMap.insert( std::make_pair<std::string, MsgPointer>( it->first, msgEntity  )  );
//		UnLockServerAckMap();
//		sock.send_to( boost::asio::buffer( mm.c_str(), mm.length() ) , *(it->second) );
//		sock.close();

	}

}

void FillInServers()
{
	ConfigFile cfg("../config/Servers.cfg");

	int ServerCount;
	std::string NONE = "NONE";

	ServerCount = StringToInt( cfg.getValueOfKey( "ServerCount", NONE ) );
	m_NumberOfServerInSystem = ServerCount;

	std::string ServerName ;
	std::string IP_PORT;
	std::string IP;
	std::string PORT;
	int idx ;

	for( int Server = 1 ; Server <= ServerCount ; Server++ )
	{
		ServerName	= "Server_" + IntToString( Server );
		if( ServerName == m_strName )
		{
			LOGGER<<"Oooops, This is me !!! \n";
			continue;
		}
		IP_PORT		= cfg.getValueOfKey( ServerName , NONE );
		idx			= IP_PORT.find_first_of( ':' );
		IP  		= IP_PORT.substr( 0, idx );
		PORT		= IP_PORT.substr( idx+ 1 , IP_PORT.length() );
		LOGGER<<"Inserting IP: "<<IP<<" Port: "<<PORT<<"\n";
		ENDPOINT_POINTER ep( new udp::endpoint( boost::asio::ip::address::from_string( IP ), atoi(PORT.c_str() ) ) );
		m_Server_Endpoint.insert( std::make_pair<std::string,ENDPOINT_POINTER>( ServerName , ep ) );
		Connection_End_Server_UDP::ConnectionEnd_Server_Pointer ser( new Connection_End_Server_UDP( m_ServerIdx++, *(ep),m_io_service_ , ServerName ) ) ;
		m_Servers.insert(  std::make_pair< std::string ,Connection_End_Server_UDP::ConnectionEnd_Server_Pointer >( ServerName , ser ) );
		ser->m_bIsConnected = false;
	}

}
void PushToAllClients()
	{

		LOGGER<<"Entering PushToAllClients\n";
		CLIENTS_MAP::iterator it = m_Clients.begin();
		MsgPointer msg( new Message() );
		msg->m_type			= REQ;
		msg->m_action		= PUSH;
		msg->m_strSenderID	= m_strName;
		time_t Datatime;
		std::string val;
		std::string strMsg;

		for( ; it != m_Clients.end() ; it++ )
		{
			LOGGER<<"Sending data to Client"<< it->second->m_ClientName <<"\n";
			bool available = m_repo.GetData( it->second->m_BindType , Datatime, val );
			msg->ID				= IntToString( it->second->m_LastMessageID++ );
			msg->m_strSenderID	= m_strName;
			msg->data			= val;
			msg->m_DataType		= it->second->m_BindType;
			if( available && !(it->second->m_bNotResponding) )
			{
				SendDataToClient( msg , it->second->remote_endpoint_ , it->second->m_ClientName , true );
			}
		}

	}

//Sends a puh message for the dat that was requested before by the client before the data was available.
void 	PushNonAnsweredPulls()
{
	LockNonAnsweredMap();

	//LOGGER<<"Inside PushNonAnsweredPulls \n";

	CLIENTNAME_DATATYPE::iterator it = m_NonAnsweredPulls.begin();

	MsgPointer msg( new Message() );
	msg->m_type		= REQ;
	msg->m_action		= PUSH;
	msg->m_strSenderID	= m_strName;
	time_t Datatime;
	std::string val;
	std::string strMsg;

	for( ; it != m_NonAnsweredPulls.end() ; it++ )
	{
		bool available = m_repo.GetData( it->second , Datatime, val );
		if( available )
		{
			msg->ID				= m_Clients[ it->first ]->m_LastMessageID++;
			msg->m_strSenderID	= m_strName;
			msg->data			= val;
			LOGGER<<"Sending Data that was required by client "<< it->first<<"\n";
			SendDataToClient( msg ,  m_Clients[ it->first ]->remote_endpoint_ , it->first , true );
		}
	}

	LOGGER<<"m_NonAnsweredPulls.size(): " << m_NonAnsweredPulls.size()<<"\n";
	m_NonAnsweredPulls.clear();
	UnLockNonAnsweredMap();
}


void HandlePush(const boost::system::error_code& err)
{
	//LOGGER<<"Entering Pusher Thread\n";

	if( err !=  boost::asio::error::operation_aborted )
	{

		LockClientMap();

		if( ( m_repo.m_LastDataUpdate > m_tLastpushTime ) && ( m_Clients.size()  > 0 )  )
		{
			PushToAllClients();
			//LOGGER<< "PusherThread: Before calling 	PushNonAnsweredPulls();\n";
			PushNonAnsweredPulls();
			//LOGGER<< "PusherThread: returning from PushNonAnsweredPulls();\n";
			m_tLastpushTime = time( NULL );
		}
		else
		{
			//LOGGER<<"No New data Available \n";
		}
		UnLockClientsMap();
		StartPush();
	}
	else
	{
		LOGGER<<" Pull operation Interrupted  \n";
	}
	//LOGGER<<"Leaving Pusher Thread\n";
}

void StartPush()
{
	pushTimer.expires_from_now( boost::posix_time::seconds( pushPeriod ) );
	pushTimer.async_wait( boost::bind( &udp_server::HandlePush ,this, boost::asio::placeholders::error ) );
}


bool LockClientMap()
{
	int SUCCESS = 0;
	int status = pthread_mutex_lock( &m_ClientsMutex);
	return ( status == SUCCESS );
}

bool UnLockClientsMap()
{
	int SUCCESS = 0;
	int status = pthread_mutex_unlock( &m_ClientsMutex );
	return ( status == SUCCESS );
}

bool LockNonAnsweredMap()
{
	int SUCCESS = 0;
	int status = pthread_mutex_lock( &m_NonAnsweredMapMutex);
	return ( status == SUCCESS );
}

bool UnLockNonAnsweredMap()
{
	int SUCCESS = 0;
	int status = pthread_mutex_unlock( &m_NonAnsweredMapMutex );
	return ( status == SUCCESS );
}

bool LockSensorMap()
{
	int SUCCESS = 0;
	int status = pthread_mutex_lock( &m_SensorsMutext);
	return ( status == SUCCESS );
}

bool UnLockSensoMap()
{
	int SUCCESS = 0;
	int status = pthread_mutex_unlock( &m_SensorsMutext);
	return ( status == SUCCESS );
}

bool  LockSocket()
{
	int SUCCESS = 0;
	int status = pthread_mutex_lock( &m_SockMutex );
	return ( status == SUCCESS );
}

bool UnLockSocket()
{
	int SUCCESS = 0;
	int status = pthread_mutex_unlock( &m_SockMutex );
	return ( status == SUCCESS );
}

bool  LockServerMap()
{
	int SUCCESS = 0;
	int status = pthread_mutex_lock( &m_ServerMapMutex );
	return ( status == SUCCESS );
}

bool UnLockServerMap()
{
	int SUCCESS = 0;
	int status = pthread_mutex_unlock( &m_ServerMapMutex );
	return ( status == SUCCESS );
}



void start_receive()
	{
	  memset( buf, 0, TERRAINPACKETSIZE );

	  socket_.async_receive_from(
		boost::asio::buffer( buf, MsgLengh ), remote_endpoint_,
		boost::bind(&udp_server::handle_receive, this,
		boost::asio::placeholders::error,
		boost::asio::placeholders::bytes_transferred));
	}

void SendAck( MsgPointer msg, udp::endpoint& ep  )
{
	udp::socket sock( m_io_service_ );
	msg->m_type 		= ACK;
	msg->m_strSenderID	= m_strName;
	std::string mm		= msg->Encode();
	sock.connect( ep );
	sock.send_to( boost::asio::buffer( mm.c_str(), mm.length() ) , ep );
	sock.close();
}

//This Function is only used
//To send data to the the sensors, so save the sensors name for resenting.
//TODO: Must reform this function.
void SendDataToClient( MsgPointer msg, udp::endpoint& ep , std::string ClientName ,  bool waitForAck )
{
	if( waitForAck )
	{
		//Insert into the Map of non Ack Messages that is in the client.
		LOGGER<<"waitForAck = "<<std::boolalpha<< waitForAck<<"\n";
		m_Clients[ ClientName ]->m_NonAckMessages.insert( std::make_pair<int,MsgPointer>( StringToInt( msg->ID ), msg ) );
	}
	std::string mm = msg->Encode();
	udp::socket sock( m_io_service_ );
	sock.connect( ep );
	//LOGGER<<"before SendDataToClient sock.send_to\n";
	LOGGER<<"Sending: "<<mm<<"\n";
	sock.send_to( boost::asio::buffer( mm.c_str(), mm.length() ) , ep );
	sock.close();
}

//TODO: Must reform this function.
void SendDataToServer( MsgPointer msg, udp::endpoint& ep , std::string ServerName ,  bool waitForAck )
{

	if( waitForAck )
	{
		m_Servers[ ServerName ]->m_NonAckMessages.insert( std::make_pair< int,MsgPointer >(StringToInt( msg->ID ), msg  ) );
	}
	std::string mm = msg->Encode();
	udp::socket sock( m_io_service_ );
	sock.connect( ep );
	LOGGER<<"Sending to "<< ServerName<<": "<<mm<<"\n";
	sock.send_to( boost::asio::buffer( mm.c_str(), mm.length() ) , ep );
	sock.close();

}
void HandleAck( MsgPointer  ReceievedMessage )
{
	if( ReceievedMessage->m_action == ENTITY  || ReceievedMessage->m_action == UPDATE )
	{
		//TODO The only time a server receives ACK for an Entity is when it was sent by the remote server in response for the begnining.
		//Or in response to an update Data message, but then the action will be update.
		//LOGGER<<"Receieved ack for a Server Entity Message \n";
		LockServerMap();
		//LOGGER<<"Acknoledging "<<m_ServerAckMap[ ReceievedMessage->m_strSenderID ]->Encode()<<"\n";
		//m_ServerAckMap[ ReceievedMessage->m_strSenderID ]->m_bAcknowledged = true;
		SERVERS_MAP::iterator serit = m_Servers.find( ReceievedMessage->m_strSenderID );
		if( serit == m_Servers.end() )
		{
			LOGGER<<"Error, Server with name" << ReceievedMessage->m_strSenderID <<" Not Found in server list\n";
			UnLockServerMap();
			return;
		}

		Connection_End_Server_UDP::ID_MSGPOINTER::iterator msgit = serit->second->m_NonAckMessages.find( StringToInt( ReceievedMessage->ID ) );
		if( msgit == serit->second->m_NonAckMessages.end() )
		{
			UnLockServerMap();
			return ;
		}
		LOGGER<<"Acknowledging "<<msgit->second->Encode()<<"\n";
		msgit->second->m_bAcknowledged = true ;
		UnLockServerMap();
	}
	else
	{
		//TODO make sure that the sender is client.
		LockClientMap();
		CLIENTS_MAP::iterator it;
		it = m_Clients.find( ReceievedMessage->m_strSenderID );
		if( it == m_Clients.end() )
		{
			LOGGER<<"Error, Client not found \n";
			LOGGER<<"SenderID"<< ReceievedMessage->m_strSenderID <<"\n" ;
			UnLockClientsMap();
			return;
		}
		Connection_End_Client_UDP::ID_MSGPOINTER::iterator msgit =  it->second->m_NonAckMessages.find( StringToInt( ReceievedMessage->ID ) );
		if( msgit == it->second->m_NonAckMessages.end() )
		{
			//LOGGER<<"Message"<< ReceievedMessage->ID <<" was not sent in client"<< ReceievedMessage->m_strSenderID<<"\n";
			LOGGER<<"Ignoring Message: "<< ReceievedMessage->Encode()<<" From sender: "<< ReceievedMessage->m_strSenderID<<"\n";
			UnLockClientsMap();
			return;
		}
		LOGGER<<"Acknowledging: "<<msgit->second->Encode()<<"\n";
		time( &msgit->second->m_ackTime ) ;
		msgit->second->m_bAcknowledged = true ;
		it->second->m_bNotResponding = false;
		UnLockClientsMap();
	}

}
void   HandleEntity( MsgPointer ReceievedMessage )
{
	Connectiontype type = GetConnectioType( ReceievedMessage->data );

	if( type == CLIENT)
	{
			LOGGER<<"New client is joining the group \n";
			Connection_End_Client_UDP::ConnectionEnd_Client_Pointer new_client( new Connection_End_Client_UDP( m_ClientIdx++ , m_repo,remote_endpoint_ ,m_io_service_ , ReceievedMessage->m_strSenderID ) );
			LockClientMap();
			m_Clients.insert( std::make_pair<std::string, Connection_End_Client_UDP::ConnectionEnd_Client_Pointer >( ReceievedMessage->m_strSenderID, new_client ) );
			UnLockClientsMap();
			new_client->m_BindType = m_DataType;
			SendAck( ReceievedMessage , new_client->remote_endpoint_ );
	}
	else if( SENSOR == type )
	{
			LOGGER<<"New Sensor Joining the Group \n";
			Connection_End_Sensor_UDP::ConnectionEnd_Sensor_Pointer new_sensor( new Connection_End_Sensor_UDP( m_SensorIdx++, m_repo , remote_endpoint_ ) );
			LockSensorMap();
			m_Sensors.insert( std::make_pair<std::string, Connection_End_Sensor_UDP::ConnectionEnd_Sensor_Pointer >( ReceievedMessage->m_strSenderID, new_sensor ) );
			UnLockSensoMap();
			SendAck( ReceievedMessage , new_sensor->remote_endpoint_ );
	}
	else if( SERVER ==type )
	{
		LOGGER<<"New Server Joining the Group \n";
		//Send Ack
		ReceievedMessage->m_type		= ACK;
		SendAck( ReceievedMessage , *(m_Server_Endpoint[ ReceievedMessage->m_strSenderID ] ));

	}
	else if( Unknown == type )
	{
			LOGGER<<"ERROR UNIDENTIFIED ENTITY\n";
	}

}

//This function takes a data type, check what server has it and sends it a pull request.
bool PullFromOtherServer( DataType DT )
{
	LOGGER<<"Pull from other Server \n";
	//Look for a server that has  the data.
	std::map< DataType, std::string >::iterator it = m_DataType_Server.find( DT );
	if( it != m_DataType_Server.end() )
	{
		//send Pull to that Server.
		MsgPointer Pull( new Message() );
		Pull->ID 			= IntToString( m_Servers[ it->second ]->m_LastMessageID++ );
		Pull->m_type		= REQ;
		Pull->m_DataType	= DT;
		Pull->m_action		= PULL;
		Pull->data			= "Server";//Important for the other server to know that the message is coming from a server.
		Pull->m_strSenderID	= m_strName;

		SendDataToServer( Pull, *( m_Server_Endpoint[ it->second ] ) ,it->second,true );
		return true;
	}
	return false;
}

void HandlePull( MsgPointer ReceievedMessage )
{
	Connectiontype conn;

	//TODO : This is a limitation in the desifn that I plan to overcome,
	if( ReceievedMessage->data == "Server")
	{
		conn = SERVER;
	}
	else
	{
		conn = CLIENT;
	}

	if( conn == CLIENT )
	{
		time_t		tm;
		std::string val;

		//TODO Post Message
		//Get the ID of the sender to get his remote end point.
		//Get the Connection Object.

		CLIENTS_MAP::iterator it;
		it = m_Clients.find( ReceievedMessage->m_strSenderID );
		if( it == m_Clients.end() )
		{
			LOGGER<<"Error, Client not found \n";
			LOGGER<<"SenderID"<< ReceievedMessage->m_strSenderID <<"\n" ;
			return;
		}
		//Update the bind for this client.
		//it->second->m_BindType = ReceievedMessage->m_DataType;

		bool available = m_repo.GetData( ReceievedMessage->m_DataType , tm, val );
		Connection_End_Client_UDP::ConnectionEnd_Client_Pointer cc = it->second;
		MsgPointer SubmitData( new Message() );

		//Keep the message ID the same for the submit data message because
		//It acknowledges the Pull message by the client.
		SubmitData->ID				= ReceievedMessage->ID;
		SubmitData->m_type			= RES;
		SubmitData->m_DataType		= ReceievedMessage->m_DataType;
		SubmitData->m_action		= SUBMIT;
		SubmitData->m_strSenderID	= m_strName;

		if( available )
		{
			SubmitData->data		= val;
		}
		else
		{
			if( m_bIsDistributed )
			{
				//Try to ask other servers.
				SubmitData->data = "Data Not Available in this server,asking another server that has it will push";
				PullFromOtherServer( ReceievedMessage->m_DataType );
				//Add the message to the non answered pulls so that you can send a Push once a data is available.
				LockNonAnsweredMap();
				m_NonAnsweredPulls.insert( std::make_pair<std::string, DataType>( ReceievedMessage->m_strSenderID, ReceievedMessage->m_DataType ) );
				UnLockNonAnsweredMap();
			}
			else
			{

				LOGGER<<"Data Not Available,will push once it is \n";
				SubmitData->data = "Data Not Available,will push once it is ";
				//Insert the message in the map to handle it later if the data becomes available.
				LockNonAnsweredMap();
				m_NonAnsweredPulls.insert( std::make_pair<std::string, DataType>( ReceievedMessage->m_strSenderID, ReceievedMessage->m_DataType ) );
				UnLockNonAnsweredMap();
			}
		}
		//No need for acknoledgement in case of submit. pull-->Submit is a complete Scenario.
		SendDataToClient( SubmitData , cc->remote_endpoint_  , cc->m_ClientName , false );
	}
	else
	{
		// A server asked for that.
		time_t		tm;
		std::string val;
		//Connection is a Server.
		bool available = m_repo.GetData( ReceievedMessage->m_DataType , tm, val );
		MsgPointer SubmitData( new Message() );

		//Keep the message ID the same for the submit data message because
		//It acknowledges the Pull message by the client.
		SubmitData->ID				= ReceievedMessage->ID;
		SubmitData->m_type		= REQ;//RES
		SubmitData->m_DataType		= ReceievedMessage->m_DataType;
		SubmitData->m_action		= SUBMIT;
		SubmitData->m_strSenderID	= m_strName;
		SubmitData->data			= val;
		//No need for an ACK , the Pull scenario is: pull--->submit data.
		SendDataToServer( SubmitData , *( m_Server_Endpoint[ ReceievedMessage->m_strSenderID ] )  , ReceievedMessage->m_strSenderID , false );
	}
}

void HandleUpdate( MsgPointer ReceievedMessage )
{

	//Update Messge coming from server.
	//Now we know that this server  has this data type.
	m_DataType_Server.insert( std::make_pair< DataType, std::string >( ReceievedMessage->m_DataType, ReceievedMessage->m_strSenderID ) );
	ReceievedMessage->m_type = ACK;
	SERVERS_MAP::iterator serit = m_Servers.find( ReceievedMessage->m_strSenderID );
	if( serit == m_Servers.end() )
	{
		LOGGER<<"Error, receieved an update message from a server not in my list ";
	}

	SendAck( ReceievedMessage, serit->second->remote_endpoint_  );

}

void SendUpdateToServers( DataType TT )
{

	LOGGER<<"Sending Update to servers \n";
	SERVERS_MAP::iterator serit = m_Servers.begin();

	for( ; serit != m_Servers.end(); serit++ )
	{

		MsgPointer msg( new Message() );
		msg->data 			= m_strName + " now has new data !";
		msg->m_strSenderID	= m_strName;
		msg->m_type			= REQ;//RES
		msg->m_DataType		= TT;
		msg->m_action		= UPDATE;
		msg->m_strSenderID	= m_strName;
		msg->ID				= IntToString( serit->second->m_LastMessageID );

		SendDataToServer( msg , serit->second->remote_endpoint_, serit->first, true  );
	}

}

void HandleSubmit( MsgPointer ReceievedMessage )
{
	//Recognise if the sender is a Seerver or a Client.
	//This is a limitation that I plan to overcome by adding the snder type to  the message.
	std::string temp	= ReceievedMessage->m_strSenderID;
	int Idx				= temp.find_first_of( '_' );
	temp				= temp.substr( 0, Idx  );
	bool bIsServer 		= ( "Server" == temp );

	if( bIsServer )
	{
		//Save the data in the Data Repo .
		time_t TimeFill = time( NULL );
		m_repo.SaveData( ReceievedMessage->m_DataType , TimeFill, ReceievedMessage->data );

		//Mark pull Message that I sent as Acknowledged.
		SERVERS_MAP::iterator it = m_Servers.find( ReceievedMessage->m_strSenderID );
		if( it == m_Servers.end()  )
		{
			LOGGER<<"Error, Server not found !\n";
			LOGGER<<"SenderID"<< ReceievedMessage->m_strSenderID <<"\n" ;
			return ;
		}
		LOGGER<<"Acknowledgeing :"<<it->second->m_NonAckMessages[ StringToInt( ReceievedMessage->ID ) ]->Encode()<<"\n" ;
		it->second->m_NonAckMessages[ StringToInt( ReceievedMessage->ID ) ]->m_bAcknowledged = true;
	}
	else
	{
		//TODO make sure that this is a Sensor.
		time_t TimeFill = time( NULL );
		bool isNew = m_repo.SaveData( ReceievedMessage->m_DataType , TimeFill, ReceievedMessage->data );

		if( isNew && m_bIsDistributed )
		{
			SendUpdateToServers( ReceievedMessage->m_DataType );
		}

		//m_setDatatypes.insert( ReceievedMessage->m_DataType );
		//TODO Acknowledge  the reception of the message.
		SENSORS_MAP::iterator it = m_Sensors.find( ReceievedMessage->m_strSenderID );
		if( it == m_Sensors.end() )
		{
			LOGGER<<"Error, Sensor not found !\n";
			LOGGER<<"SenderID"<< ReceievedMessage->m_strSenderID <<"\n" ;
			return ;
		}
		Connection_End_Sensor_UDP::ConnectionEnd_Sensor_Pointer pSensor = it->second;
		SendAck( ReceievedMessage, pSensor->remote_endpoint_ );
	}
}

void HandleReq( MsgPointer ReceievedMessage )
  {

	switch( ReceievedMessage->m_action )
  	{
  	case ENTITY:
  		HandleEntity( ReceievedMessage );
  		break;
  	case SUBMIT:
  		HandleSubmit( ReceievedMessage );
  		break;
  	case UPDATE:
  		HandleUpdate( ReceievedMessage );
  		break;
  	case PUSH:
  		//std::cout<<"Error, Receiving PUSH from Sensor\n";
  		break;
  	case PULL:
  		HandlePull( ReceievedMessage );
  		break;
//  	case BIND:
//  		break;
  	default:
  		LOGGER<<"Undefined Message, Dumping buffer !";
  		break;
  	}
}



void handle_receive(const boost::system::error_code& error, std::size_t /*bytes_transferred*/)
  {
    if (!error || error == boost::asio::error::message_size)
    {
        LOGGER<<"Server Received"<< buf<<"\n";
        MsgPointer ReceievedMessage( new Message( ) );
		std::string MsgBuf = buf ;

		if( ReceievedMessage->Decode( MsgBuf ) )
		{
				//std::cout<<"Message is: "<<MsgBuf<<" Length is: "<<MsgBuf.length()<<std::endl;
				std::cout<<"Message : "<<MsgBuf<<std::endl;
				switch( ReceievedMessage->m_type )
				{
					case REQ:
						HandleReq( ReceievedMessage );
						break;
					case RES:
						break;
					case ACK:
						HandleAck( ReceievedMessage );
						break;
					default:
						break;
				}
		}
		else
		{
			LOGGER<<"Error, Message decode failed";
		}
    start_receive();
    }

  }

void handle_send(boost::shared_ptr<std::string> msg/*message*/,
			  const boost::system::error_code& /*error*/,
			  std::size_t /*bytes_transferred*/)
{
  LOGGER<<"Sent Message "<< *msg;
}


void SetTimeOutTimer()
{
	timeOutTimer.expires_from_now( boost::posix_time::seconds( TimeOutPeriod ) );
	timeOutTimer.async_wait( boost::bind( &udp_server::HandleTimeout ,this, boost::asio::placeholders::error ) );
}

void HandleTimeout(const boost::system::error_code& err )
{
	if( err !=  boost::asio::error::operation_aborted )
	{
		typedef Connection_End_Client_UDP::ID_MSGPOINTER::iterator MsgIterator;
		CLIENTS_MAP::iterator client = m_Clients.begin();
		std::vector< MsgIterator > MsgsToDelete;
		LockClientMap();
		while( client != m_Clients.end() )
		{
			MsgIterator Msg ;
			if( client->second->m_NonAckMessages.size() > 0 )
			{
				Msg = client->second->m_NonAckMessages.begin() ;
				for( ; Msg != client->second->m_NonAckMessages.end() ; Msg++ )
					{
						if( Msg->second->IsTimedOut( TimeOutPeriod ) )
							{
								if( Msg->second->m_bAcknowledged == false )
								{
									LOGGER<<"Resending Message to client: "<<client->second->m_ClientName<<"\n";
									SendDataToClient( Msg->second , client->second->remote_endpoint_, client->second->m_ClientName, false );
								}
								else
								{
									MsgsToDelete.push_back( Msg );
								}
							}
					}
					//Now delete all the acknowledged messages from the list.
					std::vector< MsgIterator >::iterator VecMessages = MsgsToDelete.begin();
					for( ; VecMessages < MsgsToDelete.end() ; VecMessages++ )
					{
						client->second->m_NonAckMessages.erase( *VecMessages  );
					}
					MsgsToDelete.clear();
			}

			client++;
		}

		UnLockClientsMap();
//TODO This doesnt work well now, that is why it is commented.
//		//Handle TimeOut for Server Messages:
//			LockServerAckMap();
//			if( m_ServerAckMap.size() == 0 )
//			{
//				LOGGER<<"All acknowledged :) \n";
//			}
//			else
//			{
//				LOGGER<<"NOT All acknowledged :( \n";
//				//Resend those  with no acknoledgements and delete the acknoeledged.
//				typedef std::map< std::string , MsgPointer > STRING_MSGPOINTER;
//				STRING_MSGPOINTER::iterator it = m_ServerAckMap.begin();
//				std::vector< STRING_MSGPOINTER::iterator > ServerNamesToDeleteAck;
//				while( it != m_ServerAckMap.end() )
//				{
//					if( ! it->second->m_bAcknowledged )
//					{
//							udp::socket sock( m_io_service_ );
//							sock.connect( *( m_Server_Endpoint[ it->first ] ) );
//							sock.send_to( boost::asio::buffer( it->second->Encode().c_str() , it->second->Encode().length() ) , *( m_Server_Endpoint[ it->first ] ) );
//							sock.close();
//					}
//					else
//					{
//							//Delete Those  that were acknoleged:
//							ServerNamesToDeleteAck.push_back( it );
//					}
//				}
//				std::vector< STRING_MSGPOINTER::iterator >::iterator Oit = ServerNamesToDeleteAck.begin();
//				for( ;Oit != ServerNamesToDeleteAck.end() ; Oit++ )
//				{
//					m_ServerAckMap.erase( *Oit );
//				}
//			}
//			UnLockServerAckMap();
	}
	else
	{
		LOGGER<<"Sever::HandleTimeout Operation Interrupted  \n";
	}

	//LOGGER<<"Sever::HandleTimeout Leaving  \n";
	SetTimeOutTimer();
}

};

#endif
